﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Common.Json
{
    /// <summary>
    /// JSON解析对象
    /// </summary>
    public class JsonObject
    {
        private readonly StringComparison sComparison;
        private readonly JToken JO;
        private JsonObject(JToken jo, StringComparison Comparison)
        {
            this.sComparison = Comparison;
            this.JO = jo;
        }
        /// <summary>
        /// 转换json对象
        /// </summary>
        /// <param name="json">json字符串</param>
        /// <param name="ignoreCases">是否忽略key大小写</param>
        /// <returns></returns>
        public static JsonObject Parse(string json, bool ignoreCases = false)
        {
            JToken jo = JToken.Parse(json);
            if (jo == null)
            {
                return null;
            }
            return new JsonObject(jo, ignoreCases ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
        }
        /// <summary>
        /// 获得一个空的JsonObject
        /// </summary>
        /// <param name="ignoreCases"></param>
        /// <returns></returns>
        public static JsonObject New(bool ignoreCases = false)
        {
            return new JsonObject(new JObject(), ignoreCases ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
        }

        private JObject GetJObject()
        {
            if (this.JO is JObject jObj)
            {
                return jObj;
            }
            return null;
        }
        /// <summary>
        /// 获取Json字符串值
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public string GetString(string propertyName)
        {
            if (propertyName == null)
            {
                return null;
            }
            if (!propertyName.Contains("/"))
            {
                var jtoken = GetJObject()?.Property(propertyName, this.sComparison)?.Value;
                return GetString(jtoken);
            }
            var name = propertyName.Substring(0, propertyName.IndexOf('/'));
            if (!name.Contains("["))
            {
                return GetObject(name).GetString(propertyName.Substring(name.Length + 1));
            }
            var matches = Regex.Match(name, "^([^\\[]+)\\[(\\d+)\\]$");
            string nm = matches.Groups[1].Value;
            int index = int.Parse(matches.Groups[2].Value);
            return GetArray(nm)[index].GetString(propertyName.Substring(name.Length + 1));
        }
        private string GetString(JToken token)
        {
            if (token == null)
            {
                return null;
            }
            if (token.Type == JTokenType.Object || token.Type == JTokenType.Array || token.Type == JTokenType.Property || token.Type == JTokenType.Boolean)
            {
                return token.ToString(Formatting.None);
            }
            return token.ToString();
        }
        /// <summary>
        /// 获取整数值
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public int GetInt(string propertyName)
        {
            return Convert.ToInt32(GetString(propertyName));
        }
        /// <summary>
        /// 获取高精度十进制值
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public decimal GetDecimal(string propertyName)
        {
            return Convert.ToDecimal(GetString(propertyName));
        }
        /// <summary>
        /// 获取bool值
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public bool GetBoolean(string propertyName)
        {
            return Convert.ToBoolean(GetString(propertyName));
        }
        /// <summary>
        /// 获取时间
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public DateTime GetDateTime(string propertyName)
        {
            return Convert.ToDateTime(GetString(propertyName));
        }
        /// <summary>
        /// 获取一个Json操作对象
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public JsonObject GetObject(string propertyName)
        {
            var property = GetJObject()?.Property(propertyName, this.sComparison);
            if (property?.Value == null)
            {
                return null;
            }
            return new JsonObject(property.Value, this.sComparison);
        }
        /// <summary>
        /// 获取集合
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public JsonObject[] GetArray(string propertyName = null)
        {
            if (propertyName == null)
            {
                return JO.Type != JTokenType.Array ? null : ToArray();
            }
            return GetObject(propertyName)?.GetArray(null);
        }
        private JsonObject[] ToArray()
        {
            if (this.JO is JArray ja)
            {
                return ja.Select(x => new JsonObject(x, this.sComparison)).ToArray();
            }
            return null;
        }
        /// <summary>
        /// 追加属性
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public JsonObject Put(string propertyName, object value)
        {
            if (value is JsonObject jo)
            {
                GetJObject()?.Add(propertyName, jo.JO);
                return this;
            }
            GetJObject()?.Add(propertyName, JToken.FromObject(value));
            return this;
        }
        /// <summary>
        /// 转换为Json字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return GetString(this.JO);
        }
        /// <summary>
        /// 转换为Json字符串
        /// </summary>
        /// <param name="formatting"></param>
        /// <returns></returns>
        public string ToString(Formatting formatting)
        {
            return JO.ToString(formatting);
        }
    }
}
